`include "FETCH.v"
`include "DECODE.v"
`include "REG.v"
`include "EXECUTE.v"
`include "MEM.v"
`include "WB.v"
module Y86 (
	CLOCK, 
	mem_addr, 
	mem_data_i, 
	mem_write, 
	mem_data_o, 
	reset, 
	mem_ub, 
	mem_lb, 
	PC_TB_o, 
	INST_1_B_o, 
	INST_2_B_o, 
	STAGE,
	WAIT_o
	);
wire WAIT;

input CLOCK;

input reset;

input [15:0] mem_data_i;
output mem_ub;
output mem_lb;
output [31:0] mem_addr;
output [15:0] mem_data_o;
output mem_write;

//SAIDAS DE DEBUG DO TEST_BENCH
output reg [31:0] PC_TB_o;
output reg [7:0] INST_1_B_o;
output reg [7:0] INST_2_B_o;
output wire [31:0] STAGE;
output wire WAIT_o;

//REGISTRADORES ESPECIAIS
reg [31:0] PC = 32'b0;
reg [31:0] CC = 32'b0;


wire [31:0] mem_addr_fetch;
wire [31:0] mem_addr_mem;
wire m_ub;
wire m_lb;
wire fetch_ub;
wire fetch_lb;

/////////// fetch_values //////////////
  wire WAIT_FETCH;
  wire [3:0] icode_f;
  wire [3:0] ifun_f;
  wire [3:0] rA_f;
  wire [3:0] rB_f;
  wire [31:0] valC_f;
  wire [31:0] valP_f;
  wire [31:0] valA_f;
  wire [31:0] valB_f;
  wire [31:0] valE_f;
  wire [31:0] valM_f;
  wire Bch_f;
  reg enable_fetch = 1;
///////////////////////////////////////

/////////// decode_values /////////////
  reg WAIT_DECODE;
  reg [3:0] icode_d;
  reg [3:0] ifun_d;
  reg [3:0] rA_d;
  reg [3:0] rB_d;
  reg [31:0] valC_d;
  reg [31:0] valP_d;
  wire [31:0] valA_d;
  wire [31:0] valB_d;
  reg [31:0] valE_d;
  reg [31:0] valM_d;
  reg Bch_d;
  
  wire [3:0] srcA;
  wire [3:0] srcB;
  wire [31:0] valA_reg;
  wire [31:0] valB_reg;
///////////////////////////////////////

/////////// execute_values ////////////
  reg WAIT_EXECUTE;
  reg [3:0] icode_e;
  reg [3:0] ifun_e;
  reg [3:0] rA_e;
  reg [3:0] rB_e;
  reg [31:0] valC_e;
  reg [31:0] valP_e;
  reg [31:0] valA_e;
  reg [31:0] valB_e;
  wire [31:0] valE_e;
  reg [31:0] valM_e;
  wire Bch_e;
  
  wire [31:0] CC_e;
///////////////////////////////////////

/////////// memory_values /////////////
  wire WAIT_MEMORY;
  reg [3:0] icode_m;
  reg [3:0] ifun_m;
  reg [3:0] rA_m;
  reg [3:0] rB_m;
  reg [31:0] valC_m;
  reg [31:0] valP_m;
  reg [31:0] valA_m;
  reg [31:0] valB_m;
  reg [31:0] valE_m;
  wire [31:0] valM_m;
  reg Bch_m;
  
  reg enable_mem = 0;
///////////////////////////////////////

/////////// writeback_values //////////
  reg WAIT_WRITEBACK;
  reg [3:0] icode_w;
  reg [3:0] ifun_w;
  reg [3:0] rA_w;
  reg [3:0] rB_w;
  reg [31:0] valC_w;
  reg [31:0] valP_w;
  reg [31:0] valA_w;
  reg [31:0] valB_w;
  reg [31:0] valE_w;
  reg [31:0] valM_w;
  wire [31:0] PC_w;
  reg Bch_w;
  reg enable_wb = 0;
  
  wire [3:0] dstE_reg;
  wire [3:0] dstM_reg;
  wire [31:0] valE_reg;
  wire [31:0] valM_reg;
///////////////////////////////////////

FETCH f(.clk(CLOCK), .PC(PC), .enable(enable_fetch), .mem_data(mem_data_i), .mem_addr(mem_addr_fetch), .icode(icode_f), .ifun(ifun_f), .rA(rA_f), .rB(rB_f), .valC(valC_f), .valP(valP_f), .WAIT(WAIT_FETCH), .mem_ub(fetch_ub), .mem_lb(fetch_lb));

DECODE d(.icode(icode_d), .rA(rA_d), .rB(rB_d), .valA_in(valA_reg), .valB_in(valB_reg), .srcA(srcA), .srcB(srcB), .valA_out(valA_d), .valB_out(valB_d));

REG r(.clk(CLOCK), .srcA(srcA), .srcB(srcB), .dstE(dstE_reg), .valE(valE_reg), .dstM(dstM_reg), .valM(valM_reg), .valA(valA_reg), .valB(valB_reg), .reset(reset));

EXECUTE e(.icode(icode_e), .ifun(ifun_e), .valC(valC_e), .valA(valA_e), .valB(valB_e), .CC_i(CC), .valE(valE_e), .CC_o(CC_e), .Bch(Bch_e));

MEM m(.clk(CLOCK), .icode(icode_m), .valE(valE_m), .valA(valA_m), .valP(valP_m), .enable(enable_mem), .mem_data_i(mem_data_i), .mem_addr(mem_addr_mem), .mem_data_o(mem_data_o), .mem_write_e(mem_write), .data_o(valM_m), .WAIT(WAIT_MEMORY), .mem_ub(m_ub), .mem_lb(m_lb));

WB w(.icode(icode_w), .enable(enable_wb), .rA(rA_w), .rB(rB_w), .Bch(Bch_w), .valM_i(valM_w), .valE_i(valE_w), .valP(valP_w), .valC(valC_w), .valM_o(valM_reg), .valE_o(valE_reg), .dstM(dstM_reg), .dstE(dstE_reg), .PC(PC_w));

integer counter;

assign STAGE = counter;

assign WAIT = (counter == 0)? WAIT_FETCH: (counter == 3)? WAIT_MEMORY: 0;
assign WAIT_o = WAIT;
assign mem_addr = (counter == 3)?mem_addr_mem: mem_addr_fetch;
assign mem_ub = (counter == 3) ? m_ub : fetch_ub;
assign mem_lb = (counter == 3) ? m_lb : fetch_lb;
	      
always @ (posedge CLOCK or negedge reset) begin
	if(!reset) begin
		PC <= 0;
		CC <= 0;
		counter <= -1;
		enable_fetch <= 0;
		enable_mem <= 0;
		enable_wb <= 0;
		
		icode_w <= 0;
		ifun_w <= 0;
		rA_w <= 0;
		rB_w <= 0;
		valC_w <= 0;
		valP_w <= 0;
		valA_w <= 0;
		valB_w <= 0;
		valE_w <= 0;
		valM_w <= 0;
		Bch_w <= 0;
		
		icode_m <= 0;
		ifun_m <= 0;
		rA_m <= 0;
		rB_m <= 0;
		valC_m <= 0;
		valP_m <= 0;
		valA_m <= 0;
		valB_m <= 0;
		valE_m <= 0;
		Bch_m <= 0;
			
		icode_e <= 0;
		ifun_e <= 0;
		rA_e <= 0;
		rB_e <= 0;
		valC_e <= 0;
		valP_e <= 0;
		valA_e <= 0;
		valB_e <= 0;
		valM_e <= 0;
			
		icode_d <= 0;
		ifun_d <= 0;
		rA_d <= 0;
		rB_d <= 0;
		valC_d <= 0;
		valP_d <= 0;
		valE_d <= 0;
		valM_d <= 0;
		Bch_d <= 0;
		
		INST_1_B_o = 0;
		INST_2_B_o = 0;
		PC_TB_o = 0;
		
	end else begin
		if (!WAIT) begin
			counter = counter + 1;
			
			icode_w = icode_m;
			ifun_w = ifun_m;
			rA_w = rA_m;
			rB_w = rB_m;
			valC_w = valC_m;
			valP_w = valP_m;
			valA_w = valA_m;
			valB_w = valB_m;
			valE_w = valE_m;
			valM_w = valM_m;
			Bch_w = Bch_m;
				
			icode_m = icode_e;
			ifun_m = ifun_e;
			rA_m = rA_e;
			rB_m = rB_e;
			valC_m = valC_e;
			valP_m = valP_e;
			valA_m = valA_e;
			valB_m = valB_e;
			valE_m = valE_e;
			Bch_m = Bch_e;
				
			icode_e = icode_d;
			ifun_e = ifun_d;
			rA_e = rA_d;
			rB_e = rB_d;
			valC_e = valC_d;
			valP_e = valP_d;
			valA_e = valA_d;
			valB_e = valB_d;
			valM_e = valM_d;
				
			icode_d = icode_f;
			ifun_d = ifun_f;
			rA_d = rA_f;
			rB_d = rB_f;
			valC_d = valC_f;
			valP_d = valP_f;
			valE_d = valE_f;
			valM_d = valM_f;
			Bch_d = Bch_f;

			
			enable_fetch = 0;
			enable_mem = 0;
			enable_wb = 0;
			
			if(counter == 0) begin
				enable_fetch = 1;
			end
			
			if(counter == 1) begin
				INST_1_B_o = {icode_d, ifun_d};
				case(icode_d)
					7,8: begin
						INST_2_B_o = valC_d[15:0];
					end
					default: begin						
						INST_2_B_o = {rA_d, rB_d};				
					end
				endcase
			end
			
			if(counter == 3) begin
				CC = CC_e;
				enable_mem = 1;
			end
			
			if(counter == 4) begin
				enable_wb = 1;
			end
			
			if (counter >= 5) begin
				PC = PC_w;
				PC_TB_o = PC;
				enable_fetch = 1;
				counter = 0;
			end
		end
	end
end


endmodule